#!/usr/bin/env python
# coding: utf-8

# In[136]:


import geopandas as gpd
from shapely.geometry import Point
import shapely
import json

import asyncio
from aiohttp import web

import aiohttp_cors

import nest_asyncio
nest_asyncio.apply()


# # 初始化

# In[6]:


level1 = gpd.read_file('gadm36_CHN_1.shp', encoding="utf-8")
level2 = gpd.read_file('gadm36_CHN_2.shp', encoding="utf-8")
level3 = gpd.read_file('gadm36_CHN_3.shp', encoding="utf-8")


# In[7]:


level1 = level1[["NL_NAME_1","geometry"]]
level2 = level2[["NL_NAME_1","NL_NAME_2","geometry"]]
level3 = level3[["NL_NAME_1","NL_NAME_2","NL_NAME_3","geometry"]]


# In[8]:


level1 = level1.rename(columns = {
    "NL_NAME_1": "province"
})
level2 = level2.rename(columns = {
    "NL_NAME_1": "province",
    "NL_NAME_2": "city"
})
level3 = level3.rename(columns = {
    "NL_NAME_1": "province",
    "NL_NAME_2": "city",
    "NL_NAME_3": "area"
})


# In[9]:


#gadm数据集中名称一般按照|来划分简体、繁体 中文与拼音
level1["province"] = level1["province"].apply(lambda x: x.split("|")[-1])

level2["province"] = level2["province"].apply(lambda x: x.split("|")[-1])
level2["city"] = level2["city"].apply(lambda x: x.split("|")[0])

level3["province"] = level3["province"].apply(lambda x: x.split("|")[-1])
level3["city"] = level3["city"].apply(lambda x: x.split("|")[0])
level3["area"] = level3["area"].apply(lambda x: x.split("|")[0] if x else x)


# In[12]:


#判断有几个Polygon
level1["length"] = level1["geometry"].apply(
    lambda x: 1 if isinstance(x, shapely.geometry.polygon.Polygon) else len(x))
level2["length"] = level2["geometry"].apply(
    lambda x: 1 if isinstance(x, shapely.geometry.polygon.Polygon) else len(x))
level3["length"] = level3["geometry"].apply(
    lambda x: 1 if isinstance(x, shapely.geometry.polygon.Polygon) else len(x))


# In[15]:


#convex_hull返回包围geometry的最小凸
level1["convex"] = level1["geometry"].apply(
    lambda x: x.convex_hull)
level2["convex"] = level2["geometry"].apply(
    lambda x: x.convex_hull)
level3["convex"] = level3["geometry"].apply(
    lambda x: x.convex_hull)


# In[32]:


level3.loc[level3["area"].apply(lambda x: x==None), "area"] =     level3.loc[level3["area"].apply(lambda x: x==None), "city"]


# In[99]:


def get_geo_name(point, geo_df, level):
    """判断输入的point属于geo_df的哪一块
    先判断convex，如果匹配到多个，则同时对匹配到的geometry进行判断
    """
    try:
        limited_df = geo_df[geo_df["convex"].apply(lambda x: x.contains(point))]
        #先进行粗略过滤，如果只有一个符合条件，则直接返回
        if limited_df.shape[0] == 1:
            name = limited_df[level].iloc[0]
        else:
            name = limited_df[limited_df["geometry"].apply(lambda x: x.contains(point))][level].iloc[0]

        return name
    except:
        return ""


# In[100]:


def reverse_coder(lng, lat, level=3):
    """地理逆编码，如果经纬度不是float类型的，则转换成float类型
    """
    if isinstance(lng, float):
        lng = float(lng)
    if isinstance(lat, float):
        lat = float(lat)
        
    point = Point(lng, lat)
    province = get_geo_name(point, level1, "province")
    
    if level == 1:
        return [province]
    else:
        city_df = level2[level2["province"]==province]
        city = get_geo_name(point, city_df, "city")
        if level == 2:
            return [province, city]
        else:
            area_df = level3[level3["city"]==city]
            area = get_geo_name(point, area_df, "area")
            return [province, city, area]


# In[120]:


import traceback


# In[143]:


@asyncio.coroutine
async def interface_reverse_coder(request):
    
    try:
        info_json = await request.json() 
        level = 3
        area_name_list = []

        if "level" in info_json:
            level = info_json["level"]
        loc = info_json["loc"]
        
        for point in loc:      
            area_name_list.append(reverse_coder(point[0], point[1], level))
        
        return web.Response(text = json.dumps({
            "code": 200,
            "data": area_name_list
        }, ensure_ascii=False))
    except Exception as e:
        traceback.print_exc()
        return web.json_response({
            "code": 0,
            "data": repr(e)
        })


# In[144]:


app = web.Application()

app.router.add_route("POST", "/reverse/geocoder", interface_reverse_coder)
app.router.add_route("GET", "/reverse/geocoder", interface_reverse_coder)

# Configure default CORS settings.
cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

# Configure CORS on all routes.
for route in list(app.router.routes()):
    cors.add(route)


# In[ ]:


web.run_app(app,port=1333)

